package org.usmile.algorithms.leetcode.hard;

import java.util.ArrayDeque;

/**
 * 42. 接雨水
 *
 * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图，计算按此排列的柱子，下雨之后能接多少雨水。
 *
 * 示例 1：
 * 输入：height = [0,1,0,2,1,0,1,3,2,1,2,1]
 * 输出：6
 * 解释：上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图，在这种情况下，可以接 6 个单位的雨水（蓝色部分表示雨水）。
 *
 * 示例 2：
 * 输入：height = [4,2,0,3,2,5]
 * 输出：9
 *
 * 提示：
 * n == height.length
 * 1 <= n <= 2 * 104
 * 0 <= height[i] <= 105
 */
public class _0042 {
}

// 双指针
class _0042_Solution {
    public int trap(int[] height) {
        int n = height.length;
        if (n <= 2) {
            return 0;
        }

        int leftMax = 0;
        int rightMax = 0;

        // 注意：left 必须从 0 开始，right 必须从 n - 1 开始
        // 原因：第一根柱子或者最后一根柱子有可能是最大值
        int left = 0;
        int right = n - 1;
        int total = 0;
        while (left < right) {
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if (height[left] < height[right]) {
                total += leftMax - height[left];
                left++;
            } else {
                total += rightMax - height[right];
                right--;
            }
        }

        return total;
    }
}

class _0042_Solution1 {
    public int trap(int[] height) {
        int n = height.length;
        if (n <= 2) return 0;

        int[] leftMax = new int[n];
        leftMax[0] = height[0];
        for (int i = 1; i < n; i++) {
            // 计算第 i 根柱子的前面所有柱子的最大值
            leftMax[i] = Math.max(leftMax[i - 1], height[i - 1]);
        }

        int[] rightMax = new int[n];
        rightMax[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            // 计算第 i 根柱子的后面所有柱子的最大值
            rightMax[i] = Math.max(rightMax[i + 1], height[i + 1]);
        }

        int total = 0;
        for (int i = 1; i < height.length - 1; i++) {
            // 当前这个柱子能装的水的单位数等于 min(leftMax, rightMax) - height[i]
            int maxHeight = Math.min(leftMax[i], rightMax[i]);
            if (maxHeight > height[i])
                total += maxHeight - height[i];
        }

        return total;
    }
}

// 单调栈
class _0042_Solution2 {
    public int trap(int[] height) {
        int n = height.length;
        if (n <= 2) {
            return 0;
        }
        int total = 0;
        ArrayDeque<Integer> stack = new ArrayDeque<>();
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
                int top = stack.pop();
                if (stack.isEmpty()) {
                    break;
                }
                int leftIndex = stack.peek();
                int currWidth = i - leftIndex - 1;
                int currHeight = Math.min(height[leftIndex], height[i]) - height[top];
                total += currWidth * currHeight;
            }
            stack.push(i);
        }

        return total;
    }
}